/* 
 * Copyright (c) 2012 JSquirrelEngine
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in 
 * the Software without restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
 * Software, and to permit persons to whom the Software is furnished to do so, subject
 * to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 * 
 */

package org.jsquirrel.map;

import org.jsquirrel.geometry.Polygon;
import org.jsquirrel.math.IInterval;

/**
 * Abstract class for representing the nodes to be inserted on a {@link Space2D} implementation. This class
 * defines de key to be used on the node, the kind of payload to be stored and the intersection between
 * different nodes.
 * 
 * @author Juan David Adarve Bermudez
 *
 * @param <K> class for the key used for the node, it must be an implementation of {@link Comparable}
 * @param <O> class for the pay load stored on the node
 */
public abstract class Space2DNode<K extends Comparable<K>, O> implements Comparable<Space2DNode<K, O>> {
	
	
	//*********************************************************************************************
	// INSTANCE ATTRIBUTES
	//*********************************************************************************************
	
	/**
	 * Node key
	 */
	private final K key;
	
	/**
	 * Polygon for this node
	 */
	protected final Polygon shape;
	
	/**
	 * Node payload
	 */
	protected O payLoad;
	
	/**
	 * Interval in which this node triggers intersection to other nodes
	 */
	protected final IInterval triggerInterval;
	
	/**
	 * Interval in which this node is sensible to intersections
	 */
	protected final IInterval objectInterval;
		
	
	//*********************************************************************************************
	// CONSTRUCTORS
	//*********************************************************************************************
	
	/**
	 * Initiates the common attributes for every node: the polygon, the key and the trigger and
	 * object interval.
	 * 
	 * @param p
	 * @param ID
	 */
	public Space2DNode(Polygon p, K ID) {
		
		if(p == null) {
			throw new NullPointerException("Space2DNode.Space2DNode(Polygon, K): null polygon");
		}
		
		if(ID == null) {
			throw new NullPointerException("Space2DNode.Space2DNode(Polygon, K): null key");
		}
		
		this.key = ID;		
		shape = p;
		
		triggerInterval = new IInterval(0, 1);
		objectInterval = new IInterval(0, 1);
	}
	
	
	
	//*********************************************************************************************
	// PUBLIC METHODS
	//*********************************************************************************************
	
	@Override
	public String toString() {
		
		return "{key= " + this.getKey().toString() + " polygon= " + this.shape + "}";
	}
	
	//*********************************************************************************************
	// FINAL METHODS
	//*********************************************************************************************
	
	/**
	 * @return returns the node key
	 */
	public final K getKey() {
		return key;
	}
	
	/**
	 * 
	 * @return returns the polygon associated to this node
	 */
	public final Polygon getPolygon() {
		return shape;
	}
	
	/**
	 * Sets the values for the trigger interval of this node by copying the values
	 * from the interval parameter. If interval is null, the behavior of this method
	 * is undefined.
	 * 
	 * @param interval
	 */
	public final void setTransparencyTriggerInterval(IInterval interval) {
		
		triggerInterval.setLowerBound(interval.lowerBound());
		triggerInterval.setUpperBound(interval.upperBound());
	}
	
	/**
	 * Set the values of the trigger interval. This method does not check if
	 * lower <= upper
	 * 
	 * @param lower bound
	 * @param upper bound
	 */
	public final void setTransparencyTriggerInterval(int lower, int upper) {
		
		triggerInterval.setLowerBound(lower);
		triggerInterval.setUpperBound(upper);
	}
	
	/**
	 * Sets the values for the object interval of this node by copying the values
	 * from the interval parameter. If interval is null, the behavior of this method
	 * is undefined.
	 * 
	 * @param interval
	 */
	public final void setObjectTransparencyInterval(IInterval interval) {
		
		objectInterval.setLowerBound(interval.lowerBound());
		objectInterval.setUpperBound(interval.upperBound());
	}
	
	/**
	 * Set the values of the trigger interval. This method does not check if
	 * lower <= upper
	 * 
	 * @param lower bound
	 * @param upper bound
	 */
	public final void setObjectTransparencyInterval(int lower, int upper) {
		
		objectInterval.setLowerBound(lower);
		objectInterval.setUpperBound(upper);
	}
	
	/**
	 * Set the node payload
	 * @param object
	 */
	public final void setPayLoad(O object) {
		payLoad = object;
	}
	
	/**
	 * 
	 * @return returns the node payload
	 */
	public final O getPayLoad() {
		return payLoad;
	}
	
	
	/**
	 * Check intersection between this and other node passed as parameter. The first step
	 * in the intersection checking is to check intersection between the object interval
	 * of this node and the trigger interval of the other node and vice versa. In case of
	 * intersection the following step is the polygon intersection checking between the two nodes.
	 * 
	 * @param node to check intersection
	 * 
	 * @return intersection result. The results shall be interpreted as this node being called
	 * N1 and the parameter not N2
	 */
	public final Space2DNodeIntersectionResult checkIntersection(Space2DNode<K, O> node) {
		
		
		boolean thisIntersection = node.triggerInterval.intersects(objectInterval);
		boolean nodeIntersection = triggerInterval.intersects(node.objectInterval);
		
		if(thisIntersection && nodeIntersection) {
			return Space2DNodeIntersectionResult.INTERSECTION_BOTH;
		}
		
		if(thisIntersection) {
			return Space2DNodeIntersectionResult.N2_INTERSECTS_N1;
		}
		
		if(nodeIntersection) {
			return Space2DNodeIntersectionResult.N1_INTERSECTS_N2;
		}
		
		return Space2DNodeIntersectionResult.NO_INTERSECTION;
		
	}
	
	
}
